# ---------------------------------------------------------------------------
# interval-feature-splitter.py
#
# splits a user-specified shapefile into many child shapefiles, based on a user-specified 
# FIELD value and an interval of days specified by user.  The output child shapefiles are intended
# to be used as input into a Kernel Density Estimate function implemented by Hawth's 
# Geospatial Modelling Environment.
#
# Note: Upgraded from ArcGIS 9.3 arcgisscripting.  Craps out.  Fix in the works.
#
# Created on: Wed Feb 08 2012 12:43:56 AM
#    Author: Kyle Taylor (kyle.a.taylor@gmail.com)
#    Written for Idaho Fish and Game, 2012
#
# Usage: interval-feature-splitter.py <input shapefile> <interval in days to split shapefile> 
# ---------------------------------------------------------------------------

# Import system modules
import sys, string, os

# Sometimes needed for manipulatings dates retrieved from shapefile field values
# when the user forgot to set the date field as a String variable.  On-the-fly conversion
# here is currently a work in progress.
from datetime import datetime

# Global Options
GENERATE_GME_SCRIPT = "N"
DATE_FIELD_NAME = "Fix_Date"

if len(sys.argv) < 3:
    print "\npoint-data-splitter"
    print "----------------------------------------------------------------------------------"
    print "split a given point shapefile into multiple child shapefiles,"
    print "each filled with points over an interval of days you specify."
    print " "
    print "Usage: ./point-data-splitter <input shapefile> <interval in days> [options]\n"
    print "Options: --generate-gme-script"
    print "         --use-date-field <FIELD_NAME>  (note: default date field name is Fix_Date)"
    print "         --use-deliminator <DELIM_NAME>  If you wish to use a deliminator to split "
    print "                                         output date intervals, specify it here."
    print "\n----------------------------------------------------------------------------------\n"
    sys.exit()
else:
    # use script arguments...
    input_shp = sys.argv[1]
    interval = int(sys.argv[2])
    
    for n in range(len(sys.argv)):
        if n < 3: # skip the first two arguments.  Already processed
          n = 3
        if str(sys.argv[n]) == "--generate-gme-script":
          GENERATE_GME_SCRIPT = "Y"
        if str(sys.argv[n]) == "--use-date-field":
          DATE_FIELD_NAME = sys.argv[n+1]
          
try:
  import arcpy
except ImportError:
  print "\nfailed to load arcpy module.  Are you using a workstation with ArcGIS 10 installed?\n"
 
rows = arcpy.SearchCursor(input_shp)
row = rows.next()

# Parse the current year from the shapefile and prepare to split data
year = row.getValue(DATE_FIELD_NAME)[-4:]
print "\n Processing data for", year

# check to see that we have directories created for this year's data to act as a workspace.
# If we don't, create them.  If they exist empty them.
script_path = os.path.abspath(__file__) # Not crossplatform, but that doesn't matter.  The only client that can run the GP is Win 6.x anyway.
script_path = script_path[:script_path.rfind('\\')]

if not os.path.exists(script_path + '\\' + year):
  os.mkdir(script_path + '\\' + year)
  os.mkdir(script_path + '\\' + year + "\Point Data")
  if GENERATE_GME_SCRIPT == "Y":
    os.mkdir(script_path + '\\' + year + "\KDEs")

else:
    
  yN = raw_input(" -- files were found in our " + year + " directory structure.  Delete them (y/n)? : ")

  if yN == "y" or yN == "Y":

    # create essential subdirectories
      
    if GENERATE_GME_SCRIPT == "Y" and not os.path.exists(script_path + '\\' + year + "\KDEs"):
        os.mkdir(script_path + '\\' + year + "\KDEs")
    if not os.path.exists(script_path + '\\' + year + "\Point Data"):
        os.mkdir(script_path + '\\' + year + "\Point Data")
      
    # delete the previously generated batch script

    if GENERATE_GME_SCRIPT == "Y":
      try:
        os.unlink(script_path + '\\' + year + "\KDE_Batch_Script.txt")
      except Exception, e: 
        print e

    # delete everything else

    for the_file in os.listdir(script_path + '\\' + year + "\Point Data"):
      file_path = os.path.join(script_path + '\\' + year + "\Point Data", the_file)
      try:
        if os.path.isfile(file_path):
            os.unlink(file_path)
      except Exception, e:
        print "    ", e
          
  else:
    print "quit..."
    sys.exit()

# create our output file for KDE batch script generation
if GENERATE_GME_SCRIPT == "Y":
  kdeBatchScript = open(script_path + '\\' + year + "\KDE_Batch_Script.txt","w")

# Identify The Number of Collars We Are Working with in the Provided Shapefile
usingFloats = 0
collars = []

# determine whether the value is an integer or float, if it is using floating point precision, round the
# input variable to two places (X.XX) and use the float value as our collar.  If we are using a whole number
# value (XX.), set the collar name to this value.  Some of the shapefiles I have been working with use whole number values
# for the COLLAR_ID field, and some use integers.  The file splitter interface needs to be able to deal with both input types.

while row:
  try:

    value = row.getValue("Collar_ID")

    if(value%1 != 0): # check for a floating point
        value = "%.2f" % round(value,2) # if we are dealing with floating point nums, round to two places.
        value = str(value)
        usingFloats = 1
    else:  # using whole number COLLAR_ID
        value = str(value)[:-2]
    if value not in collars:
      collars.append(value)
    row = rows.next()
  except AttributeError:
     print "... caught an Attribute Error while trying to parse field COLLAR_ID.  Does the input shapefile contain that field?\n"
     sys.exit()
     
# Sort the collars list
collars.sort()
print "\n Found", str(len(collars)), "collars..."
print " " + str(collars)
print "\n Processing collar data...\n"

# Create an array of shapefiles that we can populate with data from our initial shapefile, each
# element corresponding to each collar found in the user-provided input shapefile

shapes = []

# iterate over our collars, creating shapefiles for each collar and splitting the corresponding collars into increments specified by user

for i in range(len(collars)):
  if usingFloats > 0:
    shapes.append(script_path + "\\" + year + "\\Point Data\\" + year + "_" + collars[i].replace(".", "_")) # .select calls don't like floating point "." chars in filenames...
  else:
    shapes.append(script_path + "\\" + year + "\\Point Data\\" + year + "_" + str(collars[i]))
    
  arcpy.Select_analysis(input_shp, shapes[i], "\"COLLAR_ID\" = " + str(collars[i]))
 
  #
  # split collars into num. day increments
  #

  # using current collar shapefile dump...
  
  thisCollar_rows = arcpy.SearchCursor(shapes[i] + ".shp") # prime the pump for our start date and last month searches
  thisCollar_row = thisCollar_rows.next()
  
  # Find starting date of shapefile, and set this day as the current day
  startDate = str(thisCollar_row.getValue(DATE_FIELD_NAME))[:-5] # trim "/YYYY" out of the first record of the table to produce "MM/DD"
  startMonth = int(startDate[:startDate.find('/')])
  startDay = int(startDate[startDate.find('/')+1:])
  lastMonthYear = int(12) 
  daysAvailable = 0
  
  curMonth = startMonth # prime the curMonth variable with what we polled from the first line of our shapefile
  curDay = startDay     # ... do the same with our curDay variable
  
  lastDayMonth = curDay # will be determined after the run of our first nested child
  EOF = 0               # EOF is reached
  
  while thisCollar_row: # parent loop iterates over the entire length of our newly made shapefile for THIS collar
    
    # 1st nested child loop characterizes start and stop points for THIS month
    while (curMonth == startMonth): 
      try:  
        lastDayMonth = curDay     # these vars will eventually inherit the day/month of the last line of the month
        lastMonthYear = curMonth  #
        
        value = str(thisCollar_row.getValue(DATE_FIELD_NAME))[:-5]
            
        curMonth = int(value[:value.find('/')])
        curDay = int(value[value.find('/')+1:])
        
        
        thisCollar_row = thisCollar_rows.Next()

        daysAvailable = (lastDayMonth - startDay) # num days available in the current month to contribute to writing our interval
        
      except AttributeError:  # thrown when we finish processing a collar file.
          if i < len(collars)-1:
            #For debugging
            #print " ... processing collar: " + collars[i+1]
            #print " ... will throw out days from end of the year for our last collar that don't fit into a", interval, "day interval."
            EOF = 1
          else:
            print "... finished processing collar data."
            print "... a GME batch script to generate KDEs can be found here: " + script_path + '\\' + year + "\KDE_Batch_Script.txt"
            sys.exit()
          break

##    #For debugging...
##    print "-[debug]-----------------------"
##    print "start Month: " + str(startMonth)
##    print "current Month: " + str(curMonth)
##    print "startDay: " + str(startDay)
##    print "lastDayMonth (so far): " + str(lastDayMonth)
##    print "lastMonthYear (so far): " + str(lastMonthYear)
##    print "daysAvailable: " + str(daysAvailable)
##    if EOF == 1:
##        print "EOF reached..."
##    print "-------------------------------"
    
    # 2nd nested child loop handles writing each interval into new shapefiles
    while (daysAvailable >= interval) and EOF != 1: 
      try:   
        # write interval for startDay to (startDay + interval)

        # to correct weird DATE_FIELD field format
        SQLstatement = DATE_FIELD_NAME + " >= date '" + str(startMonth) + "/" + str(startDay) + "/" + str(year)\
                       + "' AND " + DATE_FIELD_NAME + " < date '" + str(startMonth) + "/" + str(startDay + interval) + "/" + str(year) + "'"

        print " Collar:", collars[i], "-- SQL Query: " + SQLstatement

        arcpy.Select_analysis(shapes[i]+ ".shp", \
                           str(shapes[i] + '_' + str(startMonth) + '_' + str(startDay) + '_' + str(startMonth) + '_' + str(startDay + interval) + ".shp"), \
                           SQLstatement)
        
        # Add the shapefile we just created to our GME batch script if the user asked
        if GENERATE_GME_SCRIPT == "Y":

          kdeBatchScript.write("kde(in=" + '\"' + str(shapes[i]) + '_' + str(startMonth) + '_' + str(startDay) + '_' + str(startMonth) \
                             + '_' + str(startDay + interval) + ".shp" + '\"' + ", out=" + '\"' + script_path + '\\' + year + '\\' + "KDEs\\" + "kde_"  \
                             + collars[i] + '_' + str(startMonth) + '_' + str(startDay) + '_' + str(startMonth) + '_' + str(startDay + interval) \
                             + ".tif" + '\"' + ", bandwidth=0.0002, cellsize=0.0002, scalingfactor=0.0002593);\n")
        
          kdeBatchScript.write("contour(in=" + '\"' + script_path + '\\' + year + '\\' + "KDEs\\" + "kde_" + collars[i] + '_' + str(startMonth) + '_' + str(startDay) \
                             + '_' + str(startMonth) + '_' + str(startDay + interval) + ".tif" + '\"' + ", out=" + '\"' + script_path + '\\' + year + '\\' \
                             + "int_" + collars[i] + '_' + str(startMonth) + '_' + str(startDay) + '_' + str(startMonth) + '_' + str(startDay + interval) + ".shp" \
                             + '\"' + ", levels=\"0.95\");\n")

        # clean-up counters for this interval
        
        startDay += interval

        # sanity check to make sure our startDay doesn't end on the last day of a month.
        # You can't write such an interval.  Start from the first of the next month.
        # ** note: we may be losing a day's worth of data implementing this fix **
        
        if startDay == lastDayMonth:
          startDay = 1
        
        daysAvailable -= interval

      except RuntimeError:
        print "  ... invalid SQL statement caught for last interval.  Will attempt to continue processing,"
        print "      but pay careful attention to our output point data.  You might want to make sure the "
        print "      input shapefile didn't have weird overlaping dates or multiple years worth of data."
        break

    # a last conditional expression helps us clean-up any days available that won't fit into the space of the current month by extending
    # our SQL query into the next month by the necessary number of days
    if daysAvailable > 0 and curMonth != lastMonthYear and EOF != 1:
        
        # write interval for the days remaining in current month (startDay to lastDayMonth) and use the start of the next month
        # to fulfill the rest of the interval

        #to correct weird DATE_FIELD format
        SQLstatement =  DATE_FIELD_NAME + " >= date '" + str(startMonth) + "/" + str(startDay) + "/" + str(year) + "' AND " + DATE_FIELD_NAME + " < date '" + str(curMonth)\
                        + "/" + str(startDay + interval-lastDayMonth) + "/" + str(year) + "'"

        print " Collar:", collars[i], "-- SQL Query: " + SQLstatement
        
        arcpy.Select_analysis(shapes[i]+ ".shp", \
                           str(shapes[i] + '_' + str(startMonth) + '_' + str(startDay) + '_' + str(curMonth) + '_' + str(startDay + interval-lastDayMonth) + ".shp"), \
                           SQLstatement)

        # Add the shapefile we just created to our GME batch script for generating KDEs and Contours in ArcGIS 10
        if GENERATE_GME_SCRIPT == "Y":
          kdeBatchScript.write("kde(in=" + '\"' + str(shapes[i]) + '_' + str(startMonth) + '_' + str(startDay) + '_' + str(curMonth) \
                              + '_' + str(startDay + interval-lastDayMonth)  + ".shp" + '\"' + ", out=" + '\"' + script_path + '\\' \
                              + year + '\\' + "KDEs\\" + "kde_" + str(collars[i]) + '_' + str(startMonth) + '_' + str(startDay) + '_' + str(curMonth) \
                              + '_' + str(startDay + interval-lastDayMonth) + ".tif" + '\"' + ", bandwidth=0.0002, cellsize=0.0002, scalingfactor=0.0002593);\n")

          kdeBatchScript.write("contour(in=" + '\"' + script_path + '\\' + year + '\\' + "KDEs\\" + "kde_" + str(collars[i]) + '_' + str(startMonth) \
                             + '_' + str(startDay) + '_' + str(curMonth) + '_' + str(startDay + interval-lastDayMonth) + ".tif" + '\"' \
                             + ", out=" + '\"' + script_path + '\\' + year + '\\' + "int_" + str(collars[i]) + '_' + str(startMonth) + '_' \
                             + str(startDay) + '_' + str(curMonth) + '_' + str(startDay + interval-lastDayMonth) + ".shp" + '\"' \
                             + ", levels=\"0.95\");\n")

        startDay = (startDay + interval)-lastDayMonth
        daysAvailable = 0

    startMonth = curMonth # set a new start month that reflects the last line retrieved from our shapefile from our first loop
    EOF = 0               # clean-up our EOF variable for the next run.

  # remove the parent collar file that we split.  Reduces clutter
  os.unlink(script_path + "\\" + year + "\\Point Data\\" + year + "_" + str(collars[i]) + ".shp") 
  os.unlink(script_path + "\\" + year + "\\Point Data\\" + year + "_" + str(collars[i]) + ".dbf")
  os.unlink(script_path + "\\" + year + "\\Point Data\\" + year + "_" + str(collars[i]) + ".prj")
